<html>
<head>
  <script src="../OLLoader.js"></script>
  <script type="text/javascript">

    function test_constructor(t) {
        t.plan(8);
        var a = new OpenLayers.Protocol.HTTP({
            url: "foo"
        });

        // 4 tests
        t.eq(a.url, "foo", "constructor sets url");
        t.eq(a.options.url, a.url, "constructor copies url to options.url");
        t.eq(a.params, {}, "constructor sets params");
        t.eq(a.options.params, undefined, "constructor do not copy params to options.params");

        var params = {hello: "world"};
        var b = new OpenLayers.Protocol.HTTP({
            url: "bar",
            params: params
        });

        // 4 tests
        t.eq(b.url, "bar", "constructor sets url");
        t.eq(b.options.url, b.url, "constructor copies url to options.url");
        t.eq(b.params, params, "constructor sets params");
        t.eq(b.options.params, b.params, "constructor copies params to options.params");
    }

    function test_destroy(t) {
        t.plan(3);
        var protocol = new OpenLayers.Protocol.HTTP({
            url: "bar",
            params: {hello: "world"}
        });
        protocol.destroy();
        t.eq(protocol.options, null, "destroy nullifies options");
        t.eq(protocol.params, null, "destroy nullifies params");
        t.eq(protocol.headers, null, "destroy nullifies headers");
    }

    function test_read(t) {
        t.plan(10);
        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'params': {'k': 'foo_param'}
        });

        // fake XHR request object
        var request = {'status': 200};

        // options to pass to read
        var readOptions = {
            'url': 'bar_url',
            'params': {'k': 'bar_param'},
            'headers': {'k': 'bar_header'},
            'scope': {'hello': 'world'},
            'callback': function() {}
        };

        var response;

        protocol.handleResponse = function(resp, opt) {
            // 4 tests
            var req = resp.priv;
            t.ok(this == protocol,
                'handleResponse called with correct scope');
            t.ok(opt == readOptions,
                'handleResponse called with correct options');
            t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
                'handleResponse called with a Response object');
            t.eq(req, request,
                'handleResponse called with correct request');

            response = resp;
        };

        var _get = OpenLayers.Request.GET;

        OpenLayers.Request.GET = function(options) {
            // 5 tests
            t.eq(options.url, readOptions.url,
                'GET called with correct url in options');
            t.eq(options.params['k'], readOptions.params['k'],
                'GET called with correct params in options');
            t.eq(options.headers['k'], readOptions.headers['k'],
                'GET called with correct headers in options');
            t.eq(options.scope, undefined,
                'GET called with correct scope in options');
            t.ok(typeof options.callback == 'function',
                'GET called with a callback in options');
            t.delay_call(0.1, function() {
                options.callback(request);
                t.ok(resp == response,
                    'read returns the expected response object');        
                // cleanup
                protocol.destroy();
                OpenLayers.Request.GET = _get;
            });
            return request;
        };

        var resp = protocol.read(readOptions);

        OpenLayers.Request.GET = _get;
    }

    function test_readWithPOST(t) {
        t.plan(10);
        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'params': {'k': 'foo_param'}
        });

        // fake XHR request object
        var request = {'status': 200};

        // options to pass to read
        var readOptions = {
            'url': 'bar_url',
            'params': {'k': 'bar_param'},
            'scope': {'hello': 'world'},
            'callback': function() {},
            'readWithPOST': true
        };

        var response;

        protocol.handleResponse = function(resp, opt) {
            // 4 tests
            var req = resp.priv;
            t.ok(this == protocol,
                'handleResponse called with correct scope');
            t.ok(opt == readOptions,
                'handleResponse called with correct options');
            t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
                'handleResponse called with a Response object');
            t.eq(req, request,
                'handleResponse called with correct request');

            response = resp;
        };

        var _post = OpenLayers.Request.POST;

        OpenLayers.Request.POST = function(options) {
            // 5 tests
            t.eq(options.url, readOptions.url,
                'GET with POST called with correct url in options');
            t.eq(options.data, OpenLayers.Util.getParameterString(readOptions.params),
                'GET with POST called with correct params encoded in options');
            t.eq(options.headers, {"Content-Type": "application/x-www-form-urlencoded"},
                'GET with POST called with correct headers (application/x-www-form-urlencoded)');
            t.eq(options.scope, undefined,
                'GET with POST called with correct scope in options');
            t.ok(typeof options.callback == 'function',
                'GET with POST called with a callback in options');
            t.delay_call(0.1, function() {
                options.callback(request);
                t.ok(resp == response,
                    'read returns the expected response object');        
                // cleanup
                protocol.destroy();
                OpenLayers.Request.POST = _post;
            });
            return request;
        };

        var resp = protocol.read(readOptions);

        OpenLayers.Request.POST = _post;
    }

    function test_read_method(t) {
        t.plan(4);

        var _post = OpenLayers.Request.POST;
        OpenLayers.Request.POST = function(options) { return 'post'; }
        var _get = OpenLayers.Request.GET;
        OpenLayers.Request.GET = function(options) { return 'get'; }

        var protocol = new OpenLayers.Protocol.HTTP({});

        t.eq(protocol.read({}).priv, 'get',
            'readWithPOST is false by default');
        t.eq(protocol.read({readWithPOST: true}).priv, 'post',
            'readWithPOST can be set in read options');

        var protocol = new OpenLayers.Protocol.HTTP({readWithPOST: true});

        t.eq(protocol.read({}).priv, 'post',
            'readWithPOST can be set in constructor');
        t.eq(protocol.read({readWithPOST: false}).priv, 'get',
            'readWithPOST can be overridden in read options');

        OpenLayers.Request.POST = _post;
        OpenLayers.Request.GET = _get;
    }

    function test_read_bbox(t) {
        t.plan(6);

        var _get = OpenLayers.Request.GET;

        var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
        var filter = new OpenLayers.Filter.Spatial({
            type: OpenLayers.Filter.Spatial.BBOX,
            value: bounds,
            projection: new OpenLayers.Projection("foo")
        });
        
        // log requests
        var log, exp;
        OpenLayers.Request.GET = function(options) {
            log.push(options.params.bbox);
            return {status: 200};
        };

        // 1) issue request with default protocol
        log = [];
        new OpenLayers.Protocol.HTTP().read({filter: filter});

        t.eq(log.length, 1, "1) GET called once");
        t.ok(log[0] instanceof Array, "1) bbox param is array");
        exp = bounds.toArray();
        t.eq(log[0], exp, "1) bbox param doesn't include SRS id by default");
        
        // 2) issue request with default protocol
        log = [];
        new OpenLayers.Protocol.HTTP({srsInBBOX: true}).read({filter: filter});

        t.eq(log.length, 1, "2) GET called once");
        t.ok(log[0] instanceof Array, "2) bbox param is array");
        exp = bounds.toArray();
        exp.push("foo");
        t.eq(log[0], exp, "2) bbox param includes SRS id if srsInBBOX is true");

        OpenLayers.Request.GET = _get;        
    }

    function test_parseFeatures(t) {
        t.plan(5);

        var protocol = new OpenLayers.Protocol.HTTP();

        // test responseXML - 2 tests
        var request = {
            'responseXML': {
                'documentElement': 'xml'
            }
        };
        protocol.format = {
            'read': function(doc) {
                t.eq(doc.documentElement, 'xml',
                    'format.read called with correct doc');
                return doc.documentElement;
            }
        };
        var ret = protocol.parseFeatures(request);
        t.eq(ret, 'xml', 'parseFeatures returns expected value');

        // test responseText - 2 tests
        var request = {
            'responseText': 'text'
        };
        protocol.format = {
            'read': function(doc) {
                t.eq(doc, 'text',
                    'format.read called with correct doc');
                return doc;
            }
        };
        var ret = protocol.parseFeatures(request);
        t.eq(ret, 'text', 'parseFeatures returns expected value');

        // test empty responseText - 1 test
        var request = {
            'responseText': ''
        };
        protocol.format = {
            'read': function(doc) {
                t.fail('format.read should not be called');
            }
        };
        var ret = protocol.parseFeatures(request);
        t.eq(ret, null, 'parseFeatures returns expected value');
    }

    function test_create(t) {
        t.plan(10);
        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'format': {'write': function() {}}
        });

        // fake XHR request object
        var request = {'status': 200};

        // features to pass to create
        var features = ['feature'];

        // options to pass to create
        var createOptions = {
            'url': 'bar_url',
            'headers': {'k': 'bar_header'},
            'scope': {'hello': 'world'},
            'callback': function() {}
        };

        var response;

        protocol.handleCreate = function(resp, opt) {
            // 5 tests
            var req = resp.priv;
            t.ok(this == protocol,
                'handleCreate called with correct scope');
            t.ok(opt == createOptions,
                'handleCreate called with correct options');
            t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
                'handleCreate called with a Response object');
            t.ok(resp.reqFeatures == features,
                'handleCreate called with correct requested features in response');
            t.eq(req, request,
                'handleCreate called with correct request');

            response = resp;
        };

        var _post = OpenLayers.Request.POST;

        OpenLayers.Request.POST = function(options) {
            // 4 tests
            t.eq(options.url, createOptions.url,
                'POST called with correct url in options');
            t.eq(options.headers['k'], createOptions.headers['k'],
                'POST called with correct headers in options');
            t.eq(options.scope, undefined,
                'POST called with correct scope in options');
            t.ok(typeof options.callback == 'function',
                'POST called with a callback in options');
            // call callback - delayed because this function has to return first
            t.delay_call(0.1, function() {
                options.callback(request);
                t.ok(resp == response,
                    'create returns the expected response object');
                // cleanup
                protocol.destroy();
                OpenLayers.Request.POST = _post;
            });
            return request;
        };

        var resp = protocol.create(features, createOptions);
        
        OpenLayers.Request.POST = _post;
    }

    function test_update(t) {
        t.plan(10);
        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'format': {'write': function() {}}
        });

        // fake XHR request object
        var request = {'status': 200};

        // feature to pass to update
        var feature = {'feature':'feature'};

        // options to pass to update
        var updateOptions = {
            'url': 'bar_url',
            'headers': {'k': 'bar_header'},
            'scope': {'hello': 'world'},
            'callback': function() {}
        };

        var response;

        protocol.handleUpdate = function(resp, opt) {
            var req = resp.priv;
            // 5 tests
            t.ok(this == protocol,
                'handleUpdate called with correct scope');
            t.ok(opt == updateOptions,
                'handleUpdate called with correct options');
            t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
                'handleUpdate called with a Response object');
            t.ok(resp.reqFeatures == feature,
                'handleUpdate called with correct requested feature in response');
            t.eq(req, request,
                'handleUpdate called with correct request');

            response = resp;
        };

        var _put = OpenLayers.Request.PUT;

        OpenLayers.Request.PUT = function(options) {
            // 4 tests
            t.eq(options.url, updateOptions.url,
                'PUT called with correct url in options');
            t.eq(options.headers['k'], updateOptions.headers['k'],
                'PUT called with correct headers in options');
            t.eq(options.scope, undefined,
                'PUT called with correct scope in options');
            t.ok(typeof options.callback == 'function',
                'PUT called with a callback in options');
            // call callback - delayed because this function has to return first
            t.delay_call(0.1, function() {
                options.callback(request);
                t.ok(resp == response,
                    'update returns the expected response object');
                // cleanup
                protocol.destroy();
                OpenLayers.Request.PUT = _put;
            });
            return request;
        };

        var resp = protocol.update(feature, updateOptions);
        
        OpenLayers.Request.PUT = _put;
    }

    function test_update_featureurl(t) {

        // test that OpenLayers.Request.PUT receives the URL
        // set in the feature
        // http://trac.openlayers.org/ticket/2393#comment:11

        t.plan(1);

        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'format': {'write': function() {}}
        });

        // feature to pass to update
        var feature = {'feature':'feature', 'url': 'bar_url'};

        var _put = OpenLayers.Request.PUT;

        OpenLayers.Request.PUT = function(options) {
            t.eq(options.url, feature.url,
                'PUT called with correct url in options');
        };

        protocol.update(feature);

        OpenLayers.Request.PUT = _put;
    }

    function test_handleResponse(t) {
        t.plan(6);

        var protocol = new OpenLayers.Protocol.HTTP();

        var options, response, request, features;

        // test options - 2 tests
        var scope = {'fake': 'scope'};
        options = {
            'scope': scope,
            'callback': function(resp) {
                t.ok(this == scope,
                    '[no status] callback called with correct scope');
                t.ok(resp == response,
                    '[no status] callback called with correct response');
            }
        };
        response = {priv: {}};
        protocol.handleResponse(response, options);
        
        // test failure condition - 1 test
        options = {
            'callback': function(resp) {
                t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
                    '[status 400] callback called with correct response code');
            }
        };
        response = {priv: {status: 400}};
        protocol.handleResponse(response, options);

        // test success condition - 3 tests
        features = {'fake': 'features'};
        options = {
            'callback': function(resp) {
                t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
                    '[status 200] callback called with correct response code');
                t.eq(resp.features, features,
                    '[status 200] callback called with correct features in response');
            }
        };
        response = {priv: {status: 200}};
        protocol.parseFeatures = function(request) {
            t.ok(request == response.priv,
                '[status 200] parseFeatures called with correct request');
            return features;
        }
        protocol.handleResponse(response, options);

        // cleanup
        protocol.destroy();
    }

    function test_delete(t) {
        t.plan(10);
        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url'
        });

        // fake XHR request object
        var request = {'status': 200};

        // feature to pass to delete
        var feature = {'url': 'bar_url'};

        // options to pass to delete
        var deleteOptions = {
            'url': 'bar_url',
            'headers': {'k': 'bar_header'},
            'scope': {'hello': 'world'},
            'callback': function() {}
        };

        var response;

        protocol.handleDelete = function(resp, opt) {
            // 5 tests
            var req = resp.priv;
            t.ok(this == protocol,
                'handleDelete called with correct scope');
            t.ok(opt == deleteOptions,
                'handleDelete called with correct options');
            t.eq(resp.CLASS_NAME, 'OpenLayers.Protocol.Response',
                'handleDelete called with a Response object');
            t.ok(resp.reqFeatures == feature,
                'handleDelete called with correct requested feature in response');
            t.eq(req, request,
                'handleDelete called with correct request');

            response = resp;
        };

        var _delete = OpenLayers.Request.DELETE;

        OpenLayers.Request.DELETE = function(options) {
            // 4 tests
            t.eq(options.url, deleteOptions.url,
                'DELETE called with correct url in options');
            t.eq(options.headers['k'], deleteOptions.headers['k'],
                'DELETE called with correct headers in options');
            t.eq(options.scope, undefined,
                'DELETE called with correct scope in options');
            t.ok(typeof options.callback == 'function',
                'DELETE called with a callback in options');
            // call callback - delayed because this function has to return first
            t.delay_call(0.1, function() {
                options.callback(request);
                t.ok(resp == response,
                    'read returns the expected response object');
                // cleanup
                protocol.destroy();
                OpenLayers.Request.DELETE = _delete;
            });
            return request;
        };

        var resp = protocol['delete'](feature, deleteOptions);

        OpenLayers.Request.DELETE = _delete;
    }

    function test_delete_featureurl(t) {

        // test that OpenLayers.Request.DELETE receives the URL
        // set in the feature
        // http://trac.openlayers.org/ticket/2393#comment:11

        t.plan(1);

        var protocol = new OpenLayers.Protocol.HTTP({
            'url': 'foo_url',
            'format': {'write': function() {}}
        });

        // feature to pass to update
        var feature = {'feature':'feature', 'url': 'bar_url'};

        var _delete = OpenLayers.Request.DELETE;

        OpenLayers.Request.DELETE = function(options) {
            t.eq(options.url, feature.url,
                'DELETE called with correct url in options');
        };

        protocol['delete'](feature);

        OpenLayers.Request.DELETE = _delete;
    }

    function test_handleDelete(t) {
        t.plan(4);

        var protocol = new OpenLayers.Protocol.HTTP();

        var options, response, request, features;

        // test options - 2 tests
        var scope = {'fake': 'scope'};
        options = {
            'scope': scope,
            'callback': function(resp) {
                t.ok(this == scope,
                    'callback called with correct scope');
                t.ok(resp == response,
                    'callback called with correct response');
            }
        };
        response = {priv: {}};
        protocol.handleDelete(response, options);
        
        // test failure condition - 1 test
        options = {
            'callback': function(resp) {
                t.eq(resp.code, OpenLayers.Protocol.Response.FAILURE,
                    'callback called with correct response code');
            }
        };
        response = {priv: {status: 400}};
        protocol.handleDelete(response, options);

        // test success condition - 1 test
        options = {
            'callback': function(resp) {
                t.eq(resp.code, OpenLayers.Protocol.Response.SUCCESS,
                    'callback called with correct response code');
            }
        };
        response = {priv: {status: 200}};
        protocol.handleDelete(response, options);

        // cleanup
        protocol.destroy();
    }

    function test_commit(t) {
        t.plan(17);

        var protocol = new OpenLayers.Protocol.HTTP();

        // 6 features
        var features = [
            {'state': OpenLayers.State.INSERT},
            {'state': OpenLayers.State.INSERT},
            {'state': OpenLayers.State.UPDATE},
            {'state': OpenLayers.State.UPDATE},
            {'state': OpenLayers.State.DELETE},
            {'state': OpenLayers.State.DELETE}
        ];

        var options = {
            'create': {
                'callback': function(resp) {
                }
            },
            'update': {
                'callback': function(resp) {
                }
            },
            'delete': {
                'callback': function(resp) {
                }
            }
        };

        var respCreate = new OpenLayers.Protocol.Response();
        var respUpdate = new OpenLayers.Protocol.Response();
        var respDelete = new OpenLayers.Protocol.Response();

        // 2 tests
        protocol['create'] = function(feature, options) {
            t.ok(options.scope == protocol,
                'create called with correct scope');
            t.ok(typeof options.callback == 'function',
                'create called with a callback in options');
            options.callback.call(options.scope, respCreate);
            return respCreate;
        };
        // 4 tests
        protocol['update'] = function(feature, options) {
            t.ok(options.scope == protocol,
                'update called with correct scope');
            t.ok(typeof options.callback == 'function',
                'update called with a callback in options');
            options.callback.call(options.scope, respUpdate);
            return respUpdate;
        };
        // 4 tests
        protocol['delete'] = function(feature, options) {
            t.ok(options.scope == protocol,
                'delete called with correct scope');
            t.ok(typeof options.callback == 'function',
                'delete called with a callback in options');
            options.callback.call(options.scope, respDelete);
            return respDelete;
        };

        var count = 0;

        // 5 tests
        protocol.callUserCallback = function(resp, opt) {
            t.ok(opt == options,
                'callUserCallback called with correction options map');
            count++;
        };

        var resp = protocol.commit(features, options);

        // 2 tests
        t.eq(count, 5, 'callUserCallback called for each request');
        t.eq(resp.length, 5, 'commit returns array with correct length');

        // cleanup
        protocol.destroy();
    }

    function test_callUserCallback(t) {
        t.plan(1);

        var protocol = new OpenLayers.Protocol.HTTP();

        var scope = {'fake': 'scope'};

        // test commit callback
        var log = {};
        var options = {
            foo: {
                callback: function() {
                    log.scope = this;
                },
                scope: scope
            }
        };
        var resp = {requestType: 'foo'};
        protocol.callUserCallback(resp, options);
        t.ok(log.scope, scope, 'correct callback called with correct scope');

    }

    function test_options(t) {
        t.plan(6);
        
        var log1 = {};

        // test that read with no options uses protocol options - 5 tests
        var url = ".";
        var headers = {};
        var params = {};
        var scope = {};
        var protocol = new OpenLayers.Protocol.HTTP({
            format: new OpenLayers.Format({
                read: function() {},
                write: function() {}
            }),
            url: url,
            headers: headers,
            params: params,
            callback: function(resp) {
                log1.callbackCalled = true;
                log1.callbackScope = this;
                log1.request = resp && resp.priv;
                log1.requestType = resp && resp.requestType;
            },
            scope: scope
        });        
        protocol.read();
        
        t.delay_call(2, function() {
            t.eq(log1.callbackCalled, true, "[read] callback called");
            t.eq(log1.callbackScope, scope, "[read] correct scope");
            t.ok(log1.request instanceof OpenLayers.Request.XMLHttpRequest, "[read] correct priv type");
            t.eq(log1.requestType, "read", "[read] correct request type");
        });
        

        // test that commit with no options uses protocol options - 2 tests
        var log2 = {called: 0};
        protocol.options.callback = function() {
            log2.called++;
            log2.scope = this;
        };
        protocol.commit([
            {state: OpenLayers.State.INSERT},
            {state: OpenLayers.State.INSERT},
            {state: OpenLayers.State.UPDATE, url: "./1"},
            {state: OpenLayers.State.UPDATE, url: "./2"},
            {state: OpenLayers.State.DELETE, url: "./3"},
            {state: OpenLayers.State.DELETE, url: "./4"}
        ]);
        t.delay_call(2, function() {
            t.eq(log2.called, 1, "[commit] Callback called once.");
            t.eq(log2.scope, scope, "[commit] Correct scope.");
        });
    }

    function test_read_global_options(t) {

        // test that calling read doesn't write params into the protocol's
        // options object, see ticket #3237

        t.plan(2);

        var protocol = new OpenLayers.Protocol.HTTP({
            url: '.',
            callback: function() {},
            params: {'a': 'a'}
        });

        // check initial state first
        t.eq(protocol.options.params, {'a': 'a'},
             'protocol params are ok at initial state');

        var filter = new OpenLayers.Filter.Comparison({
            type: OpenLayers.Filter.Comparison.EQUAL_TO,
            property: 'b',
            value: 'b'
        });
        protocol.read({filter: filter});
        t.eq(protocol.options.params, {'a': 'a'},
             "protocol params are ok after read");
    }


  </script>
</head>
<body>
</body>
</html>
